<?php
class Categories extends Dao
{
	const RETURN_TREE = 1;
	const RETURN_NO_TREE = 0;
	private static $_instance = null;
	protected $_db = null, $_cache = null;
	private $_arrStatus = array('0'=>'Inactive', '1'=>'Active');
	
	protected function __construct()
	{
		parent::__construct();
	}
	
	public static function getInstance() 
    {
        if (null === self::$_instance) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }
	
	public function getListStatus(){
		return $this->_arrStatus;
	}
	
	private function _getByParent($args, $intCurrentLevel = 0, &$arrResults = array())
	{
		try{
			if($args['category_parent'] === null){
				$cmd = $this->_db->createCommand("SELECT * FROM tbl_cms_categories WHERE category_parent IS NULL AND category_status = IFNULL(:status, category_status) ORDER BY category_position ASC");
			} else {
				$cmd = $this->_db->createCommand("SELECT * FROM tbl_cms_categories WHERE category_parent = :id AND category_status = IFNULL(:status, category_status) ORDER BY category_position ASC");
				$cmd->bindParam(':id', $args['category_parent'], PDO::PARAM_INT);
			}
			$cmd->bindParam(':status', $args['category_status'], PDO::PARAM_INT);
			$arrTemp = $cmd->queryAll();
			
			//count how many children
			if(!empty($arrResults)){
				//get current index of results
				end($arrResults);
				$last=key($arrResults);
				$arrResults[$last]['category_num_children'] = count($arrTemp);
			}
			//add children
			foreach($arrTemp as $R){
					$R['category_level'] = $intCurrentLevel;
					$arrResults[] = $R;
					if($intCurrentLevel < $args['recursive_level']){
						$args['category_parent'] = $R['category_id'];
						$this->_getByParent($args, $intCurrentLevel+1, $arrResults);
					}
			}
			return $arrResults;
		} catch(Exception $ex){
			throw new Exception($ex->getMessage());
		}
	}
	
	public function getByParent(array $args = array())
	{
		try{
			$defaults = array(
				'category_parent' => null,
				'category_status' => null,
				'recursive_level' => 255,
				'return_style' => null		
			);
			$args = $this->args_merge($defaults, $args);
			
			if($args['recursive_level'] < 0) $args['recursive_level'] = 0;
			if($args['recursive_level'] > 255) $args['recursive_level'] = 255;
			
			/*get data from cache*/
			$strCacheKey = CACHE_CATEGORY_LIST.$args['category_parent'].'_'.$args['category_status'].'_'.$args['recursive_level'].'_'.$args['return_style'];
			$arrResults = $this->_cache->get($strCacheKey);
			
			/*cache missed: get from db*/
			if($arrResults === false){
				$arrResults = array();
				$arrResults = $this->_getByParent($args, 0, $arrResults);
				
				/*format array*/
				if($args['return_style'] == self::RETURN_TREE){
					$arrResults = $this->buildTree($arrResults, $args['category_parent']);
				}
				
				/*save to cache*/
				$this->_cache->set($strCacheKey, $arrResults, DEFAULT_LONG_CACHE_EXPIRED);
				$this->setMasterCache($strCacheKey, CACHE_CATEGORY_MASTER);
			}

			return $arrResults;
		} catch(Exception $ex){
			throw new Exception($ex->getMessage());
		}			
	}
	
	private function buildTree($arrCategories, $parentId = null)
	{
		$tree = array();
		foreach ($arrCategories as $R) {
			if ($R['category_parent'] == $parentId) {
				$children = $this->buildTree($arrCategories, $R['category_id']);
				if ($children) {
					$R['children'] = $children;
				}
				$tree[] = $R;
			}
		}
		return $tree;
	}
	
	public function get($id)
	{
		try{
			/*get data from cache*/
			$strCacheKey = CACHE_CATEGORY_LIST.$id;
			$arrResults = $this->_cache->get($strCacheKey);
			
			/*cache missed: get from db*/
			if($arrResults === false){
				$arrResults = $this->_db->createCommand()->select('*')
    										  			 ->from('tbl_cms_categories')
    										  			 //->join('tbl_profile p', 'u.id=p.user_id')
											  			 ->where('category_id=:id OR category_code=:id', array(':id'=>$id))
											  			 ->queryRow();
				/*save to cache*/
				$this->_cache->set($strCacheKey, $arrResults, DEFAULT_LONG_CACHE_EXPIRED);
				$this->setMasterCache($strCacheKey, CACHE_CATEGORY_MASTER);
			}
			return $arrResults;
		} catch(Exception $ex){
			throw new Exception($ex->getMessage());
		}
	}
	
	public function insert(array $args = array())
	{
		try{
			$defaults = array(
				'category_code' => null,
				'category_name' => null,
				'category_description' => null,
				'category_created_date' => new CDbExpression('NOW()'),
				'category_creator_id' => null,
				'category_position' => null,
				'category_parent' => null,
				'category_status' => 0
			);
			$args = $this->args_merge($defaults, $args, true);			
			
			$cmd = $this->_db->createCommand();
			$intInsertId = $cmd->insert('tbl_cms_categories',$args);
			
			/*clear cache*/
			if(!empty($intInsertId)){
				$this->clearMasterCache(CACHE_CATEGORY_MASTER);	
			}			
			return $intInsertId;
		} catch(Exception $ex){
			throw new Exception($ex->getMessage());
		}
	}

	public function update($id, array $args)
	{
		try{
			$defaults = array(
				'category_code' => null,
				'category_name' => null,
				'category_description' => null,
				'category_last_modified_date' => new CDbExpression('NOW()'),
				'category_last_modifier_id' => null,
				'category_position' => null,
				'category_parent' => null,
				'category_status' => 0
			);
			$args = $this->args_merge($defaults, $args, true);			
			
			$cmd = $this->_db->createCommand();
			$intAffectedRows = $cmd->update('tbl_cms_categories',$args, 'category_id=:id', array(':id'=>$id));
			
			/*clear cache*/
			if($intAffectedRows > 0){
				$this->clearMasterCache(CACHE_CATEGORY_MASTER);	
			}
			return $intAffectedRows;			
		} catch(Exception $ex){
			throw new Exception($ex->getMessage());
		}
	}
	
	public function delete($id)
	{
		try{		
			$cmd = $this->_db->createCommand();
			$intAffectedRows = $cmd->delete('tbl_cms_categories', 'category_id=:id', array(':id'=>$id));

			/*clear cache*/
			if($intAffectedRows > 0){
				$this->clearMasterCache(CACHE_CATEGORY_MASTER);	
			}
			return $intAffectedRows;				
		} catch(Exception $ex){
			throw new Exception($ex->getMessage());
		}
	}
	
	public function swap($id1, $id2)
	{
		if( empty($id1) || empty($id2) ) throw new Exception('Empty Id');
		try{
			$strSql = " UPDATE tbl_cms_categories AS c1 INNER JOIN tbl_cms_categories AS c2 ON ( c1.category_id = :id1 AND c2.category_id = :id2 ) OR ( c1.category_id = :id2 AND c2.category_id = :id1 )
						SET
							c1.category_position = c2.category_position,
							c2.category_position = c1.category_position;";
			$cmd = $this->_db->createCommand($strSql);
			$cmd->bindParam(':id1', $id1, PDO::PARAM_INT);
			$cmd->bindParam(':id2', $id2, PDO::PARAM_INT);
			$intAffectedRows = $cmd->execute();
			/*clear cache*/
			if($intAffectedRows > 0){
				$this->clearMasterCache(CACHE_CATEGORY_MASTER);	
			}
			return $intAffectedRows;	
		} catch(Exception $ex){
			throw new Exception($ex->getMessage());
		}
	}
	
	public function updateHierarchy(array $args)
	{
		try{
			/*TODO: CHECK TRIGGER HERE*/
			$cmd = $this->_db->createCommand();
			/*remove old hierarchy*/
			$cmd->update('tbl_cms_categories', array('category_parent'=>null));
			/*fetch parents*/
			foreach($args as $parent => $items){
				/*fetch child*/
				foreach($items as $order=>$id){
					if(!empty($id)){
						/*update new hierarchy for childen*/
						$cmd->update('tbl_cms_categories', array('category_parent'=>$parent, 'category_position'=>$order+1), 'category_id=:id', array(':id'=>$id));
					}
				}
			}
			/*find root parents*/
			$arr = array();
			$parent = array_keys($args);
			foreach($parent as $k=>$id_parent){
				foreach($args as $children){				
					if(in_array($id_parent, $children)) { unset($parent[$k]); break; }
				}
			}
			/*update new hierarchy for root parents*/
			foreach($parent as $order=>$id){
				$cmd->update('tbl_cms_categories', array('category_parent'=>null, 'category_position'=>$order+1), 'category_id=:id', array(':id'=>$id));
			}
			
			/*clear cache*/
			$this->clearMasterCache(CACHE_CATEGORY_MASTER);	
			
			return 1;
		} catch(Exception $ex){
			throw new Exception($ex->getMessage());
		}
	}
}